package com.example.hotfix;

import android.content.Context;
import android.os.Environment;
import android.util.Log;


import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Arrays;

import dalvik.system.BaseDexClassLoader;
import dalvik.system.DexClassLoader;

public class FixUtils{

    private static final String TAG = "FixUtils";
    private static final String OPTIMIZE_DEX_DIR = "odex";
    private static final String DEX_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/dex";


    public void doHotFix(Context context) {
        if (context == null) {
            Log.e(TAG, "Context is null, cannot perform hotfix.");
            return;
        }

        try {
            String dexFilePath = "android.resource://" + context.getPackageName() + "/assets/classes";
            // 获取 assets 文件中的 dex 文件输入流
            InputStream inputStream = context.getAssets().open("raw/classes.dex");
            Log.e(TAG, "doHotFix: "+inputStream);
            // 将输入流转换为字节数组
            byte[] dexBytes = getBytesFromInputStream(inputStream);
            // 获取优化后的 dex 目录
            File odexFile = context.getDir(OPTIMIZE_DEX_DIR, Context.MODE_PRIVATE);
            Log.d(TAG, "Optimized dex directory: " + odexFile.getAbsolutePath());
            // 创建 DexClassLoader 加载补丁 dex 文件
            DexClassLoader dexClassLoader = new DexClassLoader(dexFilePath, odexFile.getAbsolutePath(), null, context.getClassLoader());
            // 加载补丁
            loadPatch(context.getClassLoader(), dexClassLoader, dexBytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private byte[] getBytesFromInputStream(InputStream inputStream) throws IOException {
        ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
        int bufferSize = 1024;
        byte[] buffer = new byte[bufferSize];
        int len;
        while ((len = inputStream.read(buffer)) != -1) {
            byteBuffer.write(buffer, 0, len);
        }
        return byteBuffer.toByteArray();
    }

    private void loadPatch(ClassLoader pathClassLoader, DexClassLoader dexClassLoader, byte[] dexBytes) throws Exception {
        // 获取 PathClassLoader 的 Element 数组
        Object pathElements = getDexElements(pathClassLoader);
        System.out.println("111111：" + Arrays.toString((Object[]) pathElements));
        // 获取 DexClassLoader 的 Element 数组
        Object dexElements = getDexElements(dexClassLoader);
        System.out.println("22222：" + Arrays.toString((Object[]) dexElements));
        // 合并 Element 数组
        Object combineElementArray = combineElementArray(pathElements, dexElements);
        System.out.println("新数组为：" + Arrays.toString((Object[]) combineElementArray));
        // 通过反射，将合并后的 Element 数组赋值给 PathClassLoader 中 pathList 里面的 dexElements 变量
        setDexElements(pathClassLoader, combineElementArray);
    }

    private Object getDexElements(ClassLoader classLoader) throws Exception {
        Class<?> BaseDexClassLoaderClazz = BaseDexClassLoader.class;
        Field pathListField = BaseDexClassLoaderClazz.getDeclaredField("pathList");
        pathListField.setAccessible(true);
        Object dexPathList = pathListField.get(classLoader);
        Field dexElementsField = dexPathList.getClass().getDeclaredField("dexElements");
        dexElementsField.setAccessible(true);
        return dexElementsField.get(dexPathList);
    }

    private Object combineElementArray(Object pathElements, Object dexElements) throws Exception {
        Class<?> componentType = pathElements.getClass().getComponentType();
        int i = Array.getLength(pathElements);
        int j = Array.getLength(dexElements);
        int k = i + j;
        Object result = Array.newInstance(componentType, k);
        System.arraycopy(dexElements, 0, result, 0, j);
        System.arraycopy(pathElements, 0, result, j, i);
        return result;
    }

    private void setDexElements(ClassLoader classLoader, Object value) throws Exception {
        Class<?> BaseDexClassLoaderClazz = BaseDexClassLoader.class;
        Field pathListField = BaseDexClassLoaderClazz.getDeclaredField("pathList");
        pathListField.setAccessible(true);
        Object dexPathList = pathListField.get(classLoader);
        Field dexElementsField = dexPathList.getClass().getDeclaredField("dexElements");
        dexElementsField.setAccessible(true);
        dexElementsField.set(dexPathList, value);
    }


}





